package com.leador.map.api.location;

import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import com.leador.map.api.location.core.ClientInfoUtil;
import com.leador.map.api.location.core.CoreUtil;
import com.leador.map.location.Const;

/**
 * 定位管理器。
 */
public class LocationManagerProxy {
    public static final String GPS_PROVIDER = LocationManager.GPS_PROVIDER;
    public static final String NETWORK_PROVIDER = LocationManager.NETWORK_PROVIDER;
    public static final String KEY_LOCATION_CHANGED = LocationManager.KEY_LOCATION_CHANGED;
    public static final String KEY_PROVIDER_ENABLED = LocationManager.KEY_PROVIDER_ENABLED;
    public static final String KEY_PROXIMITY_ENTERING = LocationManager.KEY_PROXIMITY_ENTERING;
    public static final String KEY_STATUS_CHANGED = LocationManager.KEY_STATUS_CHANGED;
    public static final String LocNetwork = "lbs";

    /**
     * Andriod自带的定位管理器.
     */
    private LocationManager androidLocManager = null;
    private static LocationManagerProxy _instance = null;
    private MapLocationManager mMapLocationManager = null;
    private Context context;
    private LocationListenerProxy pendingListenerProxy;
    private PendingChangeListener pendingListener;
    private ArrayList<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();
    private Hashtable<String, LocationProviderProxy> proxyProviders = new Hashtable<String, LocationProviderProxy>();
    // 下面成员变量都是和临近警告相关
    /**
     * 记录当前用户使用的provider
     */
    private String currentProvider;
    private double alertLat;
    private double alertLng;
    private boolean isAddAlert = false; // 是否添加了临近警告
    private long alertEndTime = 0;
    private double alertRadius = 0;
    private LocationListenerProxy alertListenerProxy;
    private AlertChangeListener alertListener;
    private List<PendingIntent> alertPendingIntents = new ArrayList(); // 临近警告的pendingIntent集合
    private ClientInfoUtil clientInfo;
    private String mcc;

    private LocationManagerProxy(Context context) {

        init(context);
    }

    private LocationManagerProxy(Activity context) {
        init(context.getApplicationContext());
    }

    private void init(Context context) {
        this.context = context;
        clientInfo = ClientInfoUtil.getInstance(context);
        androidLocManager = (android.location.LocationManager) context
                .getSystemService(Context.LOCATION_SERVICE);
        mMapLocationManager = MapLocationManager.getInstance(
                context.getApplicationContext(), androidLocManager);
        mcc = clientInfo.getMcc(context);

    }

    /**
     * 获取定位SDK版本。
     * @return 版本信息。
     */
    public static String getVersion(){
        return Const.strVer;
    }
    /**
     * 获取定位管理器实例。
     * @return 定位管理器。
     */
    public synchronized static LocationManagerProxy getInstance(Activity cnt) {
        if (_instance == null) {
            _instance = new LocationManagerProxy(cnt);
        }
        return _instance;
    }
    /**
     * 获取定位管理器实例。
     * @return 定位管理器。
     */
    public synchronized static LocationManagerProxy getInstance(Context context) {
        if (_instance == null) {
            _instance = new LocationManagerProxy(context);
        }
        return _instance;
    }

    /**
     * 根据给定的经纬度和半径，设定圆形的警戒区域，当设备进入或离开这个区域时注册的PendingIntent将被激活。
     * @param latitude 纬度坐标。
     * @param longitude 经度坐标。
     * @param radius 区域半径，单位米。
     * @param expiration 到期时间（单位：毫秒，取值-1代表永不过期）。
     * @param intent 用于接收地理围栏触发消息的PendingIntent，从event获取行为（出或者入等状态）。
     */
    public void addProximityAlert(double latitude, double longitude,
                                  float radius, long expiration, PendingIntent intent) {
        if (LocationProviderProxy.LocNetwork.equals(currentProvider)
                && clientInfo.isInMainLand(mcc)) {
            if (alertListenerProxy == null) {
                alertListenerProxy = new LocationListenerProxy(this);
            }
            if (alertListener == null) {
                alertListener = new AlertChangeListener();
            }
            alertListenerProxy.startListening(alertListener, 10 * 1000, radius,
                    currentProvider);
            isAddAlert = true;
            alertLat = latitude;
            alertLng = longitude;
            alertRadius = radius;
            if (expiration != -1) { // -1 表示永不过期
                long alertStartTime = CoreUtil.getCurrentTimeStampInMilly();
                alertEndTime = alertStartTime + expiration;
            }
            alertPendingIntents.add(intent);
        } else {
            if (androidLocManager != null) {
                androidLocManager.addProximityAlert(latitude, longitude,
                        radius, expiration, intent);
            }
        }
    }

    /**
     * 移除地理围栏（移除该PendingIntent下的所有位置围栏）。
     * @param intent 用于接收地理围栏触发消息的PendingIntent。
     */
    public void removeProximityAlert(PendingIntent intent) {
        if (LocationProviderProxy.LocNetwork.equals(currentProvider)
                && clientInfo.isInMainLand(mcc)) {
            if (alertListenerProxy != null) {
                alertListenerProxy.stopListening();
            }
            alertPendingIntents.remove(intent);
            alertListenerProxy = null;
            isAddAlert = false;
            alertEndTime = 0;
            alertRadius = 0;
            alertLat = 0;
            alertLng = 0;
        } else {
            if (androidLocManager != null) {
                androidLocManager.removeProximityAlert(intent);
            }
        }
    }

    /**
     * 获取最后位置。
     * @param provider providerID。
     * @return 最后一次定位的位置信息。
     */
    public MapLocation getLastKnownLocation(String provider) {
        if (LocationProviderProxy.LocNetwork.equals(provider)
                && clientInfo.isInMainLand(mcc)) {
            currentProvider = provider;
            return mMapLocationManager.getLastKnownLocation();
        } else {
            Location sysLastLoc = androidLocManager
                    .getLastKnownLocation(provider);
            if (androidLocManager != null && sysLastLoc != null) {
                return new MapLocation(sysLastLoc);
            }
        }
        return null;
    }

    /**
     * 是否可用GPS.
     * @param gpsEnable 如果为true，则将启动GPS定位。
     */
    public void setGpsEnable(boolean gpsEnable) {
        mMapLocationManager.setGpsEnable(gpsEnable);
    }


    /**
     * 注册监听。
     * @param provider 最佳提供者
     * @param minTime 最短时间。
     * @param minDistance 最小距离 单位米。
     * @param listener 位置监听器。
     */
    public void requestLocationUpdates(String provider, long minTime,
                                       float minDistance, MapLocationListener listener) {
        if(listener==null||provider==null||provider.length()==0){
            return ;
        }
        if(_instance == null){
            return ;
        }
        if (!clientInfo.isInMainLand(mcc)
                && LocationProviderProxy.LocNetwork.equals(provider)) {
            provider = "network";
        }
        currentProvider = provider;
        if (LocationProviderProxy.LocNetwork.equals(provider)
                && clientInfo.isInMainLand(mcc)) {
            mMapLocationManager.requestLocationUpdates(minTime, minDistance,
                    listener, LocationProviderProxy.LocNetwork);
        } else if (LocationManager.GPS_PROVIDER
                .equals(provider)) {
            mMapLocationManager.requestLocationUpdates(minTime, minDistance,
                    listener, LocationManager.GPS_PROVIDER);
        } else {
            androidLocManager.requestLocationUpdates(provider, minTime,
                    minDistance, listener);
        }
    }

    /**
     * 移除给定的listener位置更新
     * @param listener 位置监听器。
     */
    public void removeUpdates(final MapLocationListener listener) {

        if (listener != null) {
            if (mMapLocationManager != null) {
                mMapLocationManager.removeUpdates(listener);
            }
            androidLocManager.removeUpdates(listener);
        }
    }

    /**
     * 启动定位。
     * @param provider 定位提供者。
     * @param minTime 最短时间。
     * @param minDistance 最小距离 单位米。
     * @param pIntent 用于接收地理围栏触发消息的PendingIntent。
     */
    public void requestLocationUpdates(String provider, long minTime,
                                       float minDistance, PendingIntent pIntent) {
        if(pIntent==null||provider==null||provider.length()==0){
            return ;
        }
        if(_instance == null){
            return ;
        }
        if (LocationProviderProxy.LocNetwork.equals(provider)
                && clientInfo.isInMainLand(mcc)) {
            if (pendingListenerProxy == null) {
                pendingListenerProxy = new LocationListenerProxy(this);
            }
            if (pendingListener == null) {
                pendingListener = new PendingChangeListener();
            }
            pendingListenerProxy.startListening(pendingListener, minTime,
                    minDistance);
            pendingIntents.add(pIntent);
        } else {
            androidLocManager.requestLocationUpdates(provider, minTime,
                    minDistance, pIntent);
        }
    }

    /**
     * 移除电子围栏更新。
     * @param intent 用于接收地理围栏触发消息的PendingIntent。
     */
    public void removeUpdates(PendingIntent intent) {
        if (pendingListenerProxy != null) {
            pendingIntents.remove(intent);
            pendingListenerProxy.stopListening();
        }
        pendingListenerProxy = null;
        androidLocManager.removeUpdates(intent);
    }

    /**
     * 得到所有的Provider集合。
     * @return 所有可用定位提供者。
     */
    public List<String> getAllProviders() {
        List<String> providers = androidLocManager.getAllProviders();
        if (providers != null) {
            if (providers.contains(LocationProviderProxy.LocNetwork) == false) {
                providers.add(LocationProviderProxy.LocNetwork);
            }
        } else {
            providers = new ArrayList<String>();
            providers.add(LocationProviderProxy.LocNetwork);
            providers.addAll(androidLocManager.getAllProviders());
        }
        return providers;
    }

    /**
     * 根据给定的enabledOnly，返回符合条件的provider列表
     * @param enabledOnly  如果为true,则返回当前正在使用的定位程序。
     * @return 匹配结果provider集合。
     */
    public List<String> getProviders(boolean enabledOnly) {
        List<String> providers = androidLocManager.getProviders(enabledOnly);
        if (isProviderEnabled(LocationProviderProxy.LocNetwork)) {
            if (providers == null || providers.size() == 0) {
                providers = new ArrayList<String>();
            }
            providers.add(LocationProviderProxy.LocNetwork);
        }
        return providers;
    }


    /**
     * 根据给定的criteria和enabledOnly，返回符合条件的provider列表。
     * @param criteria 定位匹配。
     * @param enabledOnly  如果为true,则返回当前正在使用的定位程序。
     * @return 匹配结果provider集合。
     */
    public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
        List<String> providers = androidLocManager.getProviders(criteria,
                enabledOnly);
        if (providers == null || providers.size() == 0) { // fix android 2.2 sdk
            // return
            // Collections$EmptyList
            // when providers
            // size == 0.
            providers = new ArrayList<String>();
        }
        if (LocationProviderProxy.LocNetwork.equals(getBestProvider(criteria,
                enabledOnly))) {
            providers.add(LocationProviderProxy.LocNetwork);
        }
        return providers;
    }

    /**
     * 根据criteria和enabledOnly的设定，返回最优的Provider。
     * @param criteria 定位匹配。
     * @param enabledOnly 如果为true,则返回当前正在使用的定位程序。
     * @return 匹配结果provider。
     */
    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
        String bestProvider = LocationProviderProxy.LocNetwork;
        if (criteria == null) {
            return bestProvider;
        }
        LocationProviderProxy proxy = getProvider(LocationProviderProxy.LocNetwork);
        if (!proxy.meetsCriteria(criteria)) {
            bestProvider = androidLocManager.getBestProvider(criteria,
                    enabledOnly);
        }
        if (enabledOnly) {
            if (!CoreUtil.isNetworkOK(context)) {
                bestProvider = androidLocManager.getBestProvider(criteria,
                        enabledOnly);
            }
        }
        return bestProvider;
    }

    /**
     * 检测给定的provider是否是可用。
     * @param provider 定位提供者。
     * @return true 可用，false 不可用。
     */
    public boolean isProviderEnabled(String provider) {
        if (LocationProviderProxy.LocNetwork.equals(provider)) {
            return CoreUtil.isNetworkOK(context);
        }
        return androidLocManager.isProviderEnabled(provider);
    }

    // 返回指定名称的Provider.

    /**
     * 返回指定名称的Provider。
     * @param name provider名称。
     * @return 定位代理。
     */
    public LocationProviderProxy getProvider(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name不能为空！");
        }
        LocationProviderProxy proxy = null;
        if (proxyProviders.containsKey(name)) {
            proxy = proxyProviders.get(name);
        } else {
            proxy = LocationProviderProxy.makeLocationProviderProxy(
                    androidLocManager, name);
            proxyProviders.put(name, proxy);
        }
        return proxy;
    }

    /**
     * 检索gps引擎的当前状态。
     * @param status gps状态描述或者null。
     * @return  gps状态。
     */
    public GpsStatus getGpsStatus(GpsStatus status) {
        if (androidLocManager != null) {
            return androidLocManager.getGpsStatus(status);
        }
        return null;
    }

    /**
     * 移除GPS status的监听。
     * @param listener 监听器。
     */
    public void removeGpsStatusListener(GpsStatus.Listener listener) {
        if (androidLocManager != null) {
            androidLocManager.removeGpsStatusListener(listener);
        }
    }

    /**
     * 添加GPS status的监听。
     * @param listener 监听器。
     * @return 是否添加成功。
     */
    public boolean addGpsStatusListener(GpsStatus.Listener listener) {
        if (androidLocManager != null) {
            return androidLocManager.addGpsStatusListener(listener);
        }
        return false;
    }


    /**
     * 新建模拟的provider，并将其添加到激活的provider列表。
     * @param name provider名称。
     * @param requiresNetwork 是否需要网络。
     * @param requiresSatellite 是否需要卫星。
     * @param requiresCell 是否需要基站。
     * @param hasMonetaryCost 是否有钱数统计。
     * @param supportsAltitude 是否需要海拔。
     * @param supportsSpeed 是否速度。
     * @param supportsBearing 是否需要方位。
     * @param powerRequirement 能耗。
     * @param accuracy 精度。
     */
    public void addTestProvider(String name, boolean requiresNetwork,
                                boolean requiresSatellite, boolean requiresCell,
                                boolean hasMonetaryCost, boolean supportsAltitude,
                                boolean supportsSpeed, boolean supportsBearing,
                                int powerRequirement, int accuracy) {
        if (androidLocManager != null) {
            androidLocManager.addTestProvider(name, requiresNetwork,
                    requiresSatellite, requiresCell, hasMonetaryCost,
                    supportsAltitude, supportsSpeed, supportsBearing,
                    powerRequirement, accuracy);
        }
    }

    /**
     * 移除指定的模拟provider。
     * @param provider 位置提供者。
     */
    public void clearTestProviderEnabled(String provider) {
        if (androidLocManager != null) {
            androidLocManager.clearTestProviderEnabled(provider);
        }
    }

    /**
     * 移除和指定的provider相关联的Location。
     * @param provider 位置提供者。
     */
    public void clearTestProviderLocation(String provider) {
        if (androidLocManager != null) {
            androidLocManager.clearTestProviderLocation(provider);
        }
    }

    /**
     * 移除和指定的provider相关联的Status。
     * @param provider 位置提供者。
     */
    public void clearTestProviderStatus(String provider) {
        if (androidLocManager != null) {
            androidLocManager.clearTestProviderStatus(provider);
        }
    }

    /**
     * 销毁定位,在Activity销毁时调用此方法，释放定位资源.
     */
    public void destroy() {
        if (mMapLocationManager != null) {
            mMapLocationManager.destroy();
        }
        if (proxyProviders != null) {
            proxyProviders.clear();
        }
        if (pendingIntents != null) {
            pendingIntents.clear();
        }
        if (alertPendingIntents != null) {
            alertPendingIntents.clear();
        }
        proxyProviders = null;
        pendingIntents = null;
        alertPendingIntents = null;
        mMapLocationManager = null;
        _instance = null;
    }

    class PendingChangeListener implements MapLocationListener {
        @Override
        public void onLocationChanged(Location location) {
            if (pendingIntents != null && pendingIntents.size() > 0) {
                for (PendingIntent pIntent : pendingIntents) {
                    Intent intent = new Intent();
                    Bundle bundle = new Bundle();
                    bundle.putParcelable(
                            LocationManagerProxy.KEY_LOCATION_CHANGED, location);
                    intent.putExtras(bundle);
                    try {
                        pIntent.send(context, 0, intent);
                    } catch (CanceledException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

        @Override
        public void onProviderDisabled(String provider) {
        }

        @Override
        public void onLocationChanged(MapLocation location) {
            // TODO Auto-generated method stub
            if (pendingIntents != null && pendingIntents.size() > 0) {
                for (PendingIntent pIntent : pendingIntents) {
                    Intent intent = new Intent();
                    Bundle bundle = new Bundle();
                    bundle.putParcelable(
                            LocationManagerProxy.KEY_LOCATION_CHANGED, location);
                    intent.putExtras(bundle);
                    try {
                        pIntent.send(context, 0, intent);
                    } catch (CanceledException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    class AlertChangeListener implements MapLocationListener {
        @Override
        public void onLocationChanged(Location location) {
            if (isAddAlert && alertPendingIntents.size() > 0) {
                double currentLat = location.getLatitude();
                double currentLng = location.getLongitude();
                double distance = Math.abs((currentLat - alertLat)
                        * (currentLat - alertLat) + (currentLng - alertLng)
                        * (currentLng - alertLng));
                long currentTime = 0L;
                double offsetDistance = 0;
                for (PendingIntent intent : alertPendingIntents) {
                    currentTime = CoreUtil.getCurrentTimeStampInMilly();
                    if (currentTime > alertEndTime && alertEndTime != 0) {
                        removeProximityAlert(intent);
                    } else {
                        offsetDistance = Math.abs(distance - alertRadius
                                * alertRadius);
                        if (offsetDistance < 0.5) {
                            Intent startIntent = new Intent();
                            Bundle b = new Bundle();
                            b.putParcelable(
                                    LocationManagerProxy.KEY_LOCATION_CHANGED,
                                    location);
                            startIntent.putExtras(b);
                            try {
                                intent.send(context, 0, startIntent);
                            } catch (CanceledException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

        @Override
        public void onProviderDisabled(String provider) {
        }

        @Override
        public void onLocationChanged(MapLocation location) {
            // TODO Auto-generated method stub
            if (isAddAlert && alertPendingIntents.size() > 0) {
                double currentLat = location.getLatitude();
                double currentLng = location.getLongitude();
                double distance = Math.abs((currentLat - alertLat)
                        * (currentLat - alertLat) + (currentLng - alertLng)
                        * (currentLng - alertLng));
                long currentTime = 0L;
                double offsetDistance = 0;
                for (PendingIntent intent : alertPendingIntents) {
                    currentTime = CoreUtil.getCurrentTimeStampInMilly();
                    if (currentTime > alertEndTime && alertEndTime != 0) {
                        removeProximityAlert(intent);
                    } else {
                        offsetDistance = Math.abs(distance - alertRadius
                                * alertRadius);
                        if (offsetDistance < 0.5) {
                            Intent startIntent = new Intent();
                            Bundle b = new Bundle();
                            b.putParcelable(
                                    LocationManagerProxy.KEY_LOCATION_CHANGED,
                                    location);
                            startIntent.putExtras(b);
                            try {
                                intent.send(context, 0, startIntent);
                            } catch (CanceledException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }
}
